//
//  Cache.swift
//  Social
//
//  Created by jasper on 2019/3/26.
//  Copyright © 2019 shengsheng. All rights reserved.
//

import Foundation
import Disk

public protocol Cacheable {
}

public extension Cacheable where Self: Codable {
    
    func cache(identifier: CustomStringConvertible = "") {
        Self.set(self, identifier: identifier)
    }
    
    static func fetch(identifier: CustomStringConvertible = "") -> Self? {
        return get(identifier: identifier)
    }
    
    static func remove(identifier: CustomStringConvertible = "") {
        remove(forKey: String(describing: self), identifier: identifier)
    }
    
    private static func set<T: Encodable>(_ object: T,
                                          forKey key: String = String(describing: T.self),
                                          identifier id: CustomStringConvertible = "") {
        Cache.shared.set(object, forKey: "\(key)-\(id)")
    }
    
    private static func get<T: Decodable>(for key: String = String(describing: T.self),
                                          identifier id: CustomStringConvertible = "") -> T? {
        return Cache.shared.get(for: "\(key)-\(id)")
    }
    
    private static func remove(forKey key: String,
                               identifier id: CustomStringConvertible = "") {
        Cache.shared.remove(forKey: "\(key)-\(id)")
    }
}

extension Array: Cacheable where Element: Codable {}

public class Cache {
    
    public typealias Directory = Disk.Directory
    
    public static let shared = Cache(.caches, "Temp")
    
    public let directory: Directory
    public let path: String
    
    public init(_ directory: Directory, _ path: String) {
        self.directory = directory
        self.path = path
    }
    
    public func set<T: Encodable>(_ object: T, forKey key: String) {
        do {
            try Disk.save(object, to: directory, as: path + "/\(key.md5)")
        } catch {
            print(error)
        }
    }
    public func get<T: Decodable>(for key: String) -> T? {
        return try? Disk.retrieve(path + "/\(key.md5)", from: directory, as: T.self)
    }
    
    public func set(_ data: Data, forKey key: String) {
        do {
            try Disk.save(data, to: directory, as: path + "/\(key.md5)")
        } catch {
            print(error)
        }
    }
    public func get(for key: String) -> Data? {
        return try? Disk.retrieve(path + "/\(key.md5)", from: directory, as: Data.self)
    }
    
    public func remove(forKey key: String) {
        do {
            try Disk.remove(path + "/\(key.md5)", from: directory)
        } catch {
            print(error)
        }
    }
    
    public func clear() {
        do {
            try Disk.remove(path, from: directory)
        } catch {
            print(error)
        }
    }
}
